﻿/**
 * Stardust Particle Engine
 * 
 * Homepage
 * 	http://code.google.com/p/stardust-particle-engine/
 * 
 * PDF Manual
 * 	http://stardust-particle-engine.googlecode.com/svn/trunk/manual/Stardust%20Particle%20Engine%20Manual.pdf
 */

/**
 * This example demonstrates how to traverse a particle colleciton through iterators.
 * The ParticleIterator interface is a new feature in Stardust version 1.1.
 * For usage details, check out the CurveThreadRenderer class below.
 */

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import idv.cjcat.stardust.common.clocks.SteadyClock;
	import idv.cjcat.stardust.common.emitters.Emitter;
	import idv.cjcat.stardust.common.renderers.Renderer;
	import idv.cjcat.stardust.twoD.geom.Vec2D;
	import net.hires.debug.Stats;
	
	[SWF(backgroundColor = "#000000", frameRate = 60)]
	
	public class Main extends Sprite {
		
		public static const RATE:Number = 0.25;
		
		public function Main():void {
			
			var emitter:Emitter = new MyEmitter(new SteadyClock(RATE));
			var renderer:Renderer = new CurveThreadRenderer(this, new Vec2D(233, 233));
			renderer.addEmitter(emitter);
			
			addEventListener(Event.ENTER_FRAME, emitter.step);
			
			/**
			 * Note that the memory usage increases very slowly.
			 * This is because Stardust makes use of object pool for object reuse.
			 * The memory consumption is mainly due to local variables in the Emitter.step() 
			 * and Renderer.render() methods.
			 */ 
			addChild(new Stats());
		}
	}

}

//------------------------------------------------------------------------------------------------

import flash.display.Graphics;
import flash.display.Sprite;
import idv.cjcat.stardust.common.actions.Age;
import idv.cjcat.stardust.common.actions.DeathLife;
import idv.cjcat.stardust.common.clocks.Clock;
import idv.cjcat.stardust.common.events.EmitterEvent;
import idv.cjcat.stardust.common.initializers.Life;
import idv.cjcat.stardust.common.math.StardustMath;
import idv.cjcat.stardust.common.math.UniformRandom;
import idv.cjcat.stardust.common.particles.ParticleCollection;
import idv.cjcat.stardust.common.particles.ParticleIterator;
import idv.cjcat.stardust.common.renderers.Renderer;
import idv.cjcat.stardust.twoD.actions.Move;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import idv.cjcat.stardust.twoD.geom.Vec2D;
import idv.cjcat.stardust.twoD.initializers.Position;
import idv.cjcat.stardust.twoD.initializers.Velocity;
import idv.cjcat.stardust.twoD.particles.Particle2D;
import idv.cjcat.stardust.twoD.zones.LazySectorZone;
import idv.cjcat.stardust.twoD.zones.SinglePoint;

//------------------------------------------------------------------------------------------------

const LIFE_AVG:Number = 100;
const LIFE_VAR:Number = 0;
const SPEED_AVG:Number = 0;
const SPEED_VAR:Number = 3;

const LINE_COLOR_1:uint = 0xFFFFFF;
const LINE_COLOR_2:uint = 0x000000;

//------------------------------------------------------------------------------------------------

class MyEmitter extends Emitter2D {
	
	public function MyEmitter(clock:Clock) {
		super(clock);
		
		//initializers
		addInitializer(new Life(new UniformRandom(LIFE_AVG, LIFE_VAR)));
		addInitializer(new Position(new SinglePoint(233, 233)));
		addInitializer(new Velocity(new LazySectorZone(SPEED_AVG, SPEED_VAR)));
		
		//actions
		addAction(new Age());
		addAction(new DeathLife());
		addAction(new Move());
	}
}

//------------------------------------------------------------------------------------------------

class CurveThreadRenderer extends Renderer {
	
	private const INIT_R:uint = (LINE_COLOR_1 & 0xFF0000) >> 16;
	private const INIT_G:uint = (LINE_COLOR_1 & 0x00FF00) >> 8;
	private const INIT_B:uint = (LINE_COLOR_1 & 0x0000FF);
	private const FINAL_R:uint = (LINE_COLOR_2 & 0xFF0000) >> 16;
	private const FINAL_G:uint = (LINE_COLOR_2 & 0x00FF00) >> 8;
	private const FINAL_B:uint = (LINE_COLOR_2 & 0x0000FF);
	
	private var container:Sprite;
	private var center:Vec2D;
	
	public function CurveThreadRenderer(container:Sprite, center:Vec2D) {
		this.container = container;
		this.center = center;
	}
	
	override protected function render(e:EmitterEvent):void {
		var particles:ParticleCollection = e.particles;
		
		if (particles.size <= 1) return;
		
		var lenInv:Number = 1 / particles.size;
		var index:int = particles.size - 1;
		
		//gets an iterator that points to the first particle in the particle collection
		var iter:ParticleIterator = particles.getIterator();
		var p:Particle2D;
		var prevP:Particle2D;
		
		var g:Graphics = container.graphics;
		g.clear();
		g.moveTo(center.x, center.y);
		
		//check if the iterator still contains a non-null particle reference
		while (p = Particle2D(iter.particle)) {
			//interpolation parameter
			var t:Number = index * lenInv;
			var color:uint = interpolateRGB(t);
			
			//render line segment
			if (prevP) {
				g.lineStyle(0, color);
				g.curveTo(p.x, p.y, 0.5 * (p.x + prevP.x), 0.5 * (p.y + prevP.y));
			}
			
			//moves the iterator to the next particle
			iter.next();
			index--;
			prevP = p;
		}
	}
	
	private function interpolateRGB(t:Number):uint {
		var r:uint = int(StardustMath.interpolate(0, INIT_R, 1, FINAL_R, t));
		var g:uint = int(StardustMath.interpolate(0, INIT_G, 1, FINAL_G, t));
		var b:uint = int(StardustMath.interpolate(0, INIT_B, 1, FINAL_B, t));
		return (r << 16) | (g << 8) | (b);
	}
}